/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.core.windows;
import java.awt.*;
import java.awt.event.*;
import java.beans.BeanInfo;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.SwingConstants;
import org.openide.loaders.DataObject;
import org.openide.awt.UndoRedo;
import org.openide.awt.MouseUtils;
import org.openide.awt.JPopupMenuPlus;
import org.openide.TopManager;
import org.openide.actions.*;
import org.openide.windows.*;
import org.openide.util.NbBundle;
import org.openide.util.WeakListener;
import org.openide.util.Task;
import org.openide.util.HelpCtx;
import org.openide.util.RequestProcessor;
import org.openide.util.actions.*;
import org.openide.util.actions.SystemAction;
import org.openide.util.io.SafeException;
import org.openide.nodes.Node;
import org.openide.nodes.NodeListener;
import org.openide.nodes.NodeAdapter;
import org.netbeans.core.actions.DockingAction;
import org.netbeans.core.NbNodeOperation;
import org.netbeans.core.windows.util.*;
/** Implementation of multi-tabbed top component container.
* The window should be invisible if there is no component in it.
*/
final class MultiTabContainer extends JFrame
implements TopComponentContainer,
ChangeListener, ActionPerformer, PropertyChangeListener,
WindowListener {
/** generated Serialized Version UID */
static final long serialVersionUID = -5803032916590052467L;
/** Clone action */
static CloneViewAction clone =
(CloneViewAction)SystemAction.get(CloneViewAction.class);
/** Undock action */
static UndockAction undock =
(UndockAction)SystemAction.get(UndockAction.class);
/** Nexttab action */
static NextTabAction nextTab =
(NextTabAction)SystemAction.get(NextTabAction.class);
/** Previoustab action */
static PreviousTabAction prevTab =
(PreviousTabAction)SystemAction.get(PreviousTabAction.class);
/** Close view action */
static CloseViewAction closeView =
(CloseViewAction)SystemAction.get(CloseViewAction.class);
/** Docking action */
static DockingAction docking =
(DockingAction)SystemAction.get(DockingAction.class);
/** default workspace name */
static MessageFormat windowTitle;
/** default image icon for multi tabbed frame */
static Image defaultIcon;
/** Default name for the components with no name */
static String untitledComponent;
/** Time delta between two successive calls to name change method */
static final int NAME_CHANGE_DELTA = 1000;
/** Component listener that watches if the component is shown and
* if so, it sets it as the selected one */
transient ComponentListener compL;
/** Asociation with window manager implementation */
transient WindowManagerImpl wm;
/** This flag represents the internal state of visibility.
* It can be ignored if MultiObjectFrame has no TopPanel inside */
transient boolean visibility = false;
/** Is true if this Frame has focus. */
transient boolean hasFocus = false;
/** Task for renaming */
transient RequestProcessor.Task nameTask;
/** Rename performer */
transient NameChanger nameChanger;
/** The time of last call to name change method */
transient long nameChangeTime = -1;
/** JTabbedPane for this frame */
transient JTabbedPane tabc;
/** helper variable - stores selected tab in tabbed pane */
transient TopComponent wasSelected;
/** listener to the mode which owns this container */
transient PropertyChangeListener weakModeL;
/** Asociation with the mode which we represent */
ModeImpl mode;
/** Tab placement of the tabs */
int tabPlacement;
/** Maximal allowed count of contained top components */
int maxCount;
/** Mapping between container listeners and MultiTabListenerImpls.
* @associates MultiTabListenerImpl*/
HashMap clMap;
/** List of all contained top components */
ArrayList topComps;
/** Currently selected top component in this container */
TopComponent current;
/** Internal list of deserialized tc managers. This list is used
* in validateData() method, where list of deserialized top components
* is finnaly obtained */
private ArrayList tcManagers;
/** Holds the manager of selected top component,
* used only during deserialization */
private WindowManagerImpl.TopComponentManager currentTcm;
/** Holds mode and workspace name during deserialization.
* Used to restore association with the mode after deserialization */
private String modeName;
private String workspaceName;
/** manager of versioned serialization */
private static VersionSerializator serializationManager;
/** Internal status in which currently this container resides */
private int innerStatus;
/** inner status constants */
private static final int EMPTY = 0;
private static final int SINGLE = 1;
private static final int MULTI = 2;
/** Creates new multiFrame with no mode implementation asociated.
* Used during deserialization.
*/
public MultiTabContainer () {
this(null);
}
/** Creates new multi tab top component container with given
* asociated mode implementatio
*/
public MultiTabContainer (ModeImpl mode) {
super();
this.mode = mode;
innerStatus = EMPTY;
topComps = new ArrayList(10);
maxCount = 0;
wm = (WindowManagerImpl)TopManager.getDefault().getWindowManager();
initGui();
initListeners();
}
/** initializes the gui of this container */
private void initGui () {
tabc = new JTabbedPane();
tabc.setTabPlacement (SwingConstants.BOTTOM);
getContentPane().setLayout(new BorderLayout());
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
// set default image icon
setIconImage(null);
}
/* performs needed initialization */
private void initListeners () {
// listen to self window events
addWindowListener(this);
// listen to various things
tabc.addChangeListener(this);
tabc.addMouseListener(new PopupMouseImpl());
}
/** @return an array containing all top components which are currently
* managed by this container */
public synchronized TopComponent[] getTopComponents () {
int count = topComps.size();
return (TopComponent[])topComps.toArray(new TopComponent[count]);
}
/** Adds given top component to this container.
* Beware, the code must be written to ensure that top component
* will receive addNotify() before componentActivated().
* @param tc top component to add
* @return false if top component already exixts in container or
* cannot be added due to container-dependent restrictions,
* true otherwise
*/
public boolean addTopComponent (TopComponent tc) {
// check if we can add or not
if (!canAdd(tc))
return false;
// select only if top component is already contained
if (topComps.contains(tc)) {
selectView(tc);
return true;
}
topComps.add(tc);
Image icon = tc.getIcon();
// perform adding
synchronized (this) {
switch (innerStatus) {
// first component added -> act as single
case EMPTY:
innerStatus = SINGLE;
current = tc;
if (isVisible())
getContentPane().add(current);
setIconImage(icon);
break;
// second added -> turn to multi
case SINGLE:
innerStatus = MULTI;
// deactivate first
if (current.equals(TopComponent.getRegistry().getActivated()))
doActivateComponent(null);
getContentPane().remove(current);
getContentPane().add(tabc, BorderLayout.CENTER);
setIconImage(mode.getIcon());
// add first top component
Image currIcon = current.getIcon();
if (isVisible()) {
tabc.addTab(current.getName(),
currIcon == null ? null : new ImageIcon(currIcon),
current, current.getToolTipText());
}
// BEWARE, WITHOUT BREAK!
case MULTI:
if (isVisible()) {
tabc.addTab(tc.getName(),
icon == null ? null : new ImageIcon(icon),
tc, tc.getToolTipText());
}
break;
}
// PENDING
// updateUndoRedo();
refreshAfterAdding(tc);
}
// start to listen to top component's property changes
wm.findManager(tc).addPropertyChangeListener(this);
tc.addComponentListener(getCompListener());
return true;
}
/** @return True if count of contained top components is
* smaller than maxCount property */
public boolean canAdd (TopComponent tc) {
return (maxCount <= 0) || (topComps.size() < maxCount);
}
/** Sets new maximal count of top components that can be
* contained in this container */
public void setMaxCount (int maxCount) {
this.maxCount = maxCount;
}
/** @return Maximal allowed count of contained top components */
public int getMaxCount () {
return maxCount;
}
/** Removes specified top component from this container.
* Beware! The code in this method must be written to ensure
* that each top component will receive componentDeactivated() before
* removeNotify().
* @param tc top component to remove
*/
public int removeTopComponent (TopComponent tc) {
if (!topComps.contains(tc))
return topComps.size();
// stop to listen to the component
wm.findManager(tc).removePropertyChangeListener(this);
tc.removeComponentListener(getCompListener());
int count = -1;
synchronized (this) {
topComps.remove(tc);
count = topComps.size();
// update current if needed
if (tc.equals(current)) {
current = (count == 0) ? null : (TopComponent)topComps.get(0);
}
switch (innerStatus) {
// the only component removed -> turn into EMPTY
case SINGLE:
innerStatus = EMPTY;
doActivateComponent(null);
getContentPane().remove(tc);
checkVisible();
break;
case MULTI:
// deactivate first
if (tc.equals(TopComponent.getRegistry().getActivated()))
doActivateComponent(null);
if (count == 1) {
// only one component left -> turn to single
innerStatus = SINGLE;
// remove using removeTabAt to ensure correct visility reset
for (int i = tabc.getTabCount() - 1; i >= 0; i--) {
tabc.removeTabAt(i);
}
// switch pane content
tabc.invalidate();
tabc.validate();
getContentPane().remove(tabc);
setIconImage(current.getIcon());
if (isVisible()) {
current.setVisible(true);
getContentPane().add(current);
}
} else {
int index = tabc.indexOfComponent(tc);
if (index != -1) {
tabc.removeTabAt(index);
// synchronize current variable with tab selection
current = (TopComponent)tabc.getSelectedComponent();
}
// invoke relayout of multi tab
tabc.invalidate ();
tabc.validate ();
tabc.repaint();
}
// reactivate
if (hasFocus) {
doActivateComponent(current);
if (innerStatus == SINGLE) {
doFrameActivated();
}
}
updateMainTitle();
// PENDING
// getWorkspaceElement().setUndoRedo (comp.getUndoRedo ());
break;
}
}
return count;
}
/** @return true if container contains specified top component,
* false otherwise
*/
public boolean containsTopComponent (TopComponent tc) {
return topComps.contains(tc);
}
/** @return Currently selected top component or null if no top
* component is selected at a time */
public TopComponent getSelectedTopComponent() {
return current;
}
/** The component requests focus. Move it to be visible.
* When in iconified state, deiconify first.
*/
public void requestFocus (TopComponent tc) {
requestFocusBase();
selectView(tc);
}
/** The whole mode requests focus. Move it to be visible.
* When in iconified state, deiconify first. */
public void requestFocus () {
requestFocusBase();
if (current != null) {
current.requestFocus();
}
}
private void requestFocusBase () {
if (getState() == Frame.ICONIFIED)
setState(Frame.NORMAL);
toFront();
super.requestFocus();
}
/** Sets visibility status for container
* @param visible true if container should be made visible,
* false otherwise
*/
public void setVisible (boolean visible) {
visible = (innerStatus != EMPTY) ? visible : false;
if (visibility == visible)
return;
visibility = visible;
if (visibility) {
if (weakModeL == null) {
// listen to our mode
weakModeL = WeakListener.propertyChange(this, mode);
mode.addPropertyChangeListener(weakModeL);
}
// attach our top components to the AWT's hierarchy
synchronized (this) {
if (innerStatus == MULTI) {
if (getContentPane().getComponentCount() <= 0) {
getContentPane().add(tabc);
}
// add all contained top components
TopComponent curComp = null;
for (Iterator iter = topComps.iterator(); iter.hasNext(); ) {
curComp = (TopComponent)iter.next();
Image icon = curComp.getIcon();
tabc.addTab(
curComp.getName(),
(icon == null) ? null : new ImageIcon(icon),
curComp, curComp.getToolTipText()
);
}
} else if (innerStatus == SINGLE) {
if (getContentPane().getComponentCount() <= 0) {
getContentPane().add(current);
// ensure that component is visible
if (!current.isVisible())
current.setVisible(true);
}
}
}
} else {
// free our top components from AWT's component hierarchy
// to be available for other workspaces
if (innerStatus == MULTI) {
wasSelected = (TopComponent)tabc.getSelectedComponent();
// remove using removeTabAt to reset visibility flag
// properly (bug in JTabbedPane)
for (int i = tabc.getTabCount() - 1; i >= 0; i--) {
tabc.removeTabAt(i);
/*if (!curComp.isVisible())
System.out.println("Component " + curComp.getName() +
" not visible.");*/ // NOI18N
}
} else if (innerStatus == SINGLE) {
getContentPane().remove(current);
}
}
super.setVisible(visible);
if (visible) {
if ((innerStatus == MULTI) && (wasSelected != null)) {
// restore selected tab and ensure that it is visible
final TopComponent toBeSelected = wasSelected;
// select the component a little bit later,
// when tabs are all added and initialized
SwingUtilities.invokeLater(new Runnable() {
public void run () {
selectView(toBeSelected);
if (!toBeSelected.isVisible())
toBeSelected.setVisible(true);
}
}
);
}
}
}
/**
* @return true if frame is visible or false if it isn't.
*/
public boolean isVisible () {
return visibility;
}
/** Request for UI update (when changing L&F) */
public void updateUI () {
SwingUtilities.updateComponentTreeUI(this);
}
/** Adds given container listener for listening to the container
* events */
public synchronized void addContainerListener (ContainerListener cl) {
if (clMap == null)
clMap = new HashMap(5);
MultiTabListenerImpl mtli = new MultiTabListenerImpl(cl);
addComponentListener(mtli);
addWindowListener(mtli);
clMap.put(cl, mtli);
}
/** Removes given container listener */
public synchronized void removeContainerListener (ContainerListener cl) {
if (clMap == null)
return;
MultiTabListenerImpl mtli = (MultiTabListenerImpl)clMap.get(cl);
if (mtli != null) {
removeComponentListener(mtli);
removeWindowListener(mtli);
}
clMap.remove(cl);
}
/** Safe getter for component listener */
ComponentListener getCompListener () {
if (compL == null) {
compL = new ComponentAdapter () {
public void componentShown (ComponentEvent ev) {
Component comp = ev.getComponent();
/*if ((tabc.indexOfComponent(comp) >= 0) && comp.isVisible()) {
tabc.setSelectedComponent(comp);
comp.repaint();
}*/
}
};
}
return compL;
}
/** Overrides superclass' version to allow null icon parameter.
* @param icon New icon of the window.
* If icon equals to null, default icon is used.
*/
public void setIconImage (Image icon) {
if (defaultIcon == null) {
defaultIcon = Toolkit.getDefaultToolkit().getImage(
getClass().getResource(
"/org/netbeans/core/resources/frames/default.gif" // NOI18N
)
);
}
if ((icon == null) && (mode != null))
icon = mode.getIcon();
if (icon != null)
super.setIconImage(icon);
else if (getIconImage() == null)
super.setIconImage(defaultIcon);
}
/** Reactions to the property changes of contained top components
* and property changes of the mode.
*/
public void propertyChange (PropertyChangeEvent ev) {
String propName = ev.getPropertyName();
Object source = ev.getSource();
if (source instanceof WindowManagerImpl.TopComponentManager) {
// notification from top component manager
TopComponent tc = ((WindowManagerImpl.TopComponentManager)source).getComponent();
if (WindowManagerImpl.TopComponentManager.PROP_NAME.equals(propName)) {
componentNameChanged(tc);
} else
if (WindowManagerImpl.TopComponentManager.PROP_ICON.equals(propName)) {
setComponentIcon(tc, (Image)ev.getNewValue());
} else
if (WindowManagerImpl.TopComponentManager.PROP_ACTIVATED_NODES.
equals(propName)) {
// PENDING... should we do anything?
}
} else {
// notification from the mode implementation
if (ModeImpl.PROP_DISPLAY_NAME.equals(propName)) {
updateMainTitle();
}
}
}
/** Updates title of multi frame. Title is constructed from
* the multi frame name and the name of selected view
*/
private void updateMainTitle () {
if (windowTitle == null) {
ResourceBundle bundle = NbBundle.getBundle(MultiTabContainer.class);
windowTitle = new MessageFormat(bundle.getString("CTL_MultiTabTitle"));
if (untitledComponent == null)
untitledComponent = bundle.getString("CTL_UntitledComponent");
}
String selectedName = (current == null) ? "?" : current.getName(); // NOI18N
if (selectedName == null)
selectedName = untitledComponent;
switch (innerStatus) {
case SINGLE:
setTitle(selectedName);
break;
case MULTI:
setTitle(windowTitle.format(new Object[] {
mode == null ? NbBundle.getBundle(MultiTabContainer.class).
getString("CTL_UntitledMultiTab")
: mode.getDisplayName(),
selectedName })
);
break;
}
}
/** Called when the name of some component in this multi frame has changed */
void componentNameChanged (TopComponent component) {
if (!topComps.contains(component))
return; // not added yet
long now = System.currentTimeMillis();
if ((nameChangeTime < 0) ||
((now - nameChangeTime) > NAME_CHANGE_DELTA)) {
SwingUtilities.invokeLater(new NameChanger(this, component));
} else {
// run the request for the change of the name of tab
if ((nameTask == null) || (nameChanger.component != component)) {
nameTask = RequestProcessor.createRequest(
nameChanger = new NameChanger(this, component));
}
nameTask.schedule(1500);
}
nameChangeTime = now;
}
/** Called when icon of some component in this multi frame has changed */
void setComponentIcon (TopComponent component, Image icon) {
if (!topComps.contains(component))
return; // not added yet
if (innerStatus == SINGLE) {
setIconImage(icon);
} else {
int compIndex = tabc.indexOfComponent(component);
if (compIndex < 0)
return;
tabc.setIconAt(compIndex, new ImageIcon(icon));
if (isVisible()) {
tabc.invalidate ();
tabc.validate ();
tabc.repaint();
}
}
}
/** Initialization when frame becomes active */
void doFrameActivated () {
registerPerformers();
}
/** Activates component and updates selected nodes.
* If param is null, clears both active top component and
* selected nodes.
*/
void doActivateComponent (TopComponent tc) {
wm.activateComponent(tc);
// selected nodes
// don't change sel nodes if activated top component
// is property sheet
if (!(tc instanceof NbNodeOperation.Sheet)) {
RegistryImpl rimpl = (RegistryImpl) TopComponent.getRegistry();
Node[] nodes = (tc == null) ? new Node[0] : tc.getActivatedNodes();
rimpl.selectedNodesChanged(
new SelectedNodesChangedEvent(this, tc, nodes));
}
}
/** Post-initialization after adding - called from addTopComponent method */
void refreshAfterAdding (TopComponent comp) {
if (innerStatus == SINGLE) {
// first component added, perform needed initialization
// retrieve and set initial bounds, if possible
Rectangle modeBounds = mode.getBounds();
if (modeBounds != null) {
setBounds(modeBounds);
} else {
pack();
}
updateMainTitle();
}
// adds listener for closing the component
comp.addComponentListener (getCompListener());
if (isVisible() && innerStatus == MULTI) {
// workaround for JTabbedPane bug, make visible if needed
selectView(comp);
if (!comp.isVisible())
comp.setVisible(true);
tabc.invalidate ();
tabc.validate ();
tabc.repaint();
}
}
/** Helper method, called from mode managers during
* deserialization. Updates undo / redo management */
// PENDING
/*void updateUndoRedo () {
TopComponent comp = (TopComponent)tabc.getSelectedComponent();
if (comp != null)
getWorkspaceElement().setUndoRedo(comp.getUndoRedo());
}*/
/** Selects specified top component
*/
void selectView (TopComponent comp) {
if ((innerStatus == MULTI) && (tabc.indexOfComponent(comp) >= 0)) {
// select
tabc.setSelectedComponent(comp);
// ensure that component is visible
/*if (!comp.isVisible())
comp.setVisible(true);*/
}
}
/** Register as performer for actions when get focus.
*/
void registerPerformers () {
// next and previous actions
updateNextAndPreviousState();
if (innerStatus == EMPTY)
return;
// close action
closeView.setActionPerformer(this);
// undock action
if (current != null) {
// obtain mode active top component is in
Workspace curWorkspace = wm.getCurrentWorkspace();
Mode compMode = curWorkspace.findMode(current);
// activate undock action if component is in multi
if ((compMode != null) && (!((ModeImpl)compMode).isSingle()))
undock.setActionPerformer(this);
}
// clone action
if (current instanceof TopComponent.Cloneable)
clone.setActionPerformer(this);
}
/** Enable or disable next and previous actions depending on
* current inner state */
void updateNextAndPreviousState () {
if (innerStatus == MULTI) {
nextTab.setActionPerformer(this);
prevTab.setActionPerformer(this);
} else {
nextTab.setActionPerformer(null);
prevTab.setActionPerformer(null);
}
}
/** @return context help for the frame. */
public HelpCtx getHelpCtx () {
return new HelpCtx(MultiTabContainer.class);
}
/** Makes the window invisible if in EMPTY state, make it visible
* in all other cases.
*/
void checkVisible () {
super.setVisible(innerStatus != EMPTY);
}
/************* Reactions to window events ************/
/** performers registering, and component activation. */
public void windowActivated (WindowEvent e) {
hasFocus = true;
doActivateComponent(current);
doFrameActivated();
}
/** performers registering */
public void windowOpened (WindowEvent ev) {
registerPerformers();
}
/** deactivating... */
public void windowDeactivated (WindowEvent ev) {
hasFocus = false;
}
public void windowClosing (WindowEvent ev) {
}
public void windowIconified (WindowEvent ev) {
}
public void windowDeiconified (WindowEvent ev) {
}
public void windowClosed (WindowEvent ev) {
}
/** Implementation of the ChangeListener interface
* Called when some tab is the tabbed pane is activated.
*/
public void stateChanged (ChangeEvent e) {
if ((!isVisible()) || (tabc.getTabCount() <= 0) ||
(innerStatus != MULTI))
return;
int index = tabc.getSelectedIndex();
current = (index < 0) ? null : (TopComponent)tabc.getSelectedComponent();
updateMainTitle();
if (hasFocus) {
doActivateComponent(current);
doFrameActivated();
// request focus if needed
if (current != null) {
current.requestFocus();
}
}
}
/** Sets the placement of the tabs.
* @param placement SwingConstant.XXX constant representing new
* placement of the tabs (TOP, LEFT...)
*/
public void setTabPlacement (int tabPlacement) {
if (this.tabPlacement == tabPlacement)
return;
this.tabPlacement = tabPlacement;
tabc.setTabPlacement(tabPlacement);
// PENDING - need to repaint?
}
/** @return current placement of the tab */
public int getTabPlacement () {
return tabPlacement;
}
/** Implementation of the ActionPerformer interface
* Performs differently depending on given action.
*/
public void performAction (SystemAction action) {
if (current == null)
return;
if (action instanceof CloseViewAction)
// close view action performing
current.close();
else if (action instanceof CloneViewAction ||
action instanceof UndockAction) {
// prepare new single mode
WorkspaceImpl curWorkspace = (WorkspaceImpl)wm.getCurrentWorkspace();
String modeName = wm.findUnusedModeName(current.getName(), curWorkspace);
Mode singleMode = curWorkspace.createMode(
modeName, modeName, null, ModeImpl.SINGLE, true
);
if (action instanceof CloneViewAction) {
// clone the view and dock into new single mode
TopComponent newComp =
(TopComponent)((TopComponent.Cloneable)current).cloneComponent();
newComp.setIcon(current.getIcon());
singleMode.dockInto(newComp);
newComp.open();
} else {
// undock to the single mode
singleMode.dockInto(current);
}
} else if (action instanceof NextTabAction ||
action instanceof PreviousTabAction) {
// select previous or next component
int count = tabc.getTabCount();
int index = tabc.getSelectedIndex();
if ((count <= 1) || (index == -1))
return;
int delta = (action instanceof NextTabAction) ? 1 : -1;
int newIndex = (index + delta + count) % count;
tabc.setSelectedIndex(newIndex);
}
}
/** Called when whole deserialization is done,
* assign listeners to the top compoennts' property changes
*/
public void validateObject() throws java.io.InvalidObjectException {
}
/** Called when first phase of WS deserialization is done.
* Finish deserialization of all contained top components
* and attach listeners properly.
*/
public void validateData () {
// restore connection with our mode
mode = (ModeImpl)TopManager.getDefault().getWindowManager().
findWorkspace(workspaceName).findMode(modeName);
workspaceName = null;
modeName = null;
// obtain top component instances from
// instances of top component managers
topComps = new ArrayList(tcManagers.size());
WindowManagerImpl.TopComponentManager cur = null;
for (Iterator iter = tcManagers.iterator(); iter.hasNext(); ) {
cur = (WindowManagerImpl.TopComponentManager)iter.next();
if (cur.validateData()) {
topComps.add(cur.getComponent());
// set current if possible
if (cur.equals(currentTcm)) {
current = cur.getComponent();
}
}
}
// make temporary variables gc'able
tcManagers = null;
currentTcm = null;
// update current if needed
int count = topComps.size();
if ((current == null) && (count > 0)) {
current = (TopComponent)topComps.get(0);
}
// update inner status
if (count <= 0)
innerStatus = EMPTY;
else if (count == 1)
innerStatus = SINGLE;
// another validation
wasSelected = current;
updateMainTitle();
// listen to top components
TopComponent curTc = null;
for (Iterator iter = topComps.iterator(); iter.hasNext(); ) {
curTc = (TopComponent)iter.next();
wm.findManager(curTc).addPropertyChangeListener(this);
curTc.addComponentListener(getCompListener());
}
// update icon
if ((innerStatus == SINGLE) && (current != null))
setIconImage(current.getIcon());
if (innerStatus == MULTI)
setIconImage(mode.getIcon());
}
/** Let instance of properly parametrized DefaultReplacer to keep
* persistent state of this multi tab container */
private Object writeReplace ()
throws ObjectStreamException {
// provide version with data
Version1 version =
(Version1)serializationManager().getVersion(Version1.NAME);
version.assignData(this);
// use replacer
return new DefaultReplacer(new VSAccess(serializationManager()));
}
/** Accessor to the versioned serialization manager */
private VersionSerializator serializationManager () {
if (serializationManager == null) {
serializationManager = createSerializationManager();
}
return serializationManager;
}
/** Creates new serialization manager filled with our versions */
private static VersionSerializator createSerializationManager () {
VersionSerializator result = new VersionSerializator();
result.putVersion(new Version1());
return result;
}
/** Only accessor for inner class */
private void fireNameChange (final String newName) {
firePropertyChange("name", null, newName); // NOI18N
}
/** Popup menu reaction implementation */
final class PopupMouseImpl extends MouseUtils.PopupMouseAdapter {
/** Called when the seqeunce of mouse events should lead to actual
* showing of the popup menu. */
protected void showPopup (MouseEvent e) {
TabbedPaneUI tabUI = tabc.getUI();
int clickTab = tabUI.tabForCoordinate(tabc, e.getX(), e.getY());
TopComponent selected = current;
if ((selected == null) ||
(!selected.equals(TopComponent.getRegistry().getActivated()))) {
return;
}
JPopupMenu popup = new JPopupMenuPlus();
// constructs pop-up menu from actions of selected component
SystemAction[] compActions = selected.getSystemActions();
for (int i = 0; i < compActions.length; i++) {
if (compActions[i] == null)
popup.addSeparator();
else if (compActions[i] instanceof CallableSystemAction)
popup.add(((CallableSystemAction)compActions[i]).
getPopupPresenter());
}
if (compActions.length != 0)
popup.addSeparator();
// and add our docking action
popup.add(docking.getPopupPresenter());
Point p = e.getPoint ();
SwingUtilities.convertPointToScreen (p, MultiTabContainer.this);
Dimension popupSize = popup.getPreferredSize ();
Dimension screenSize = Toolkit.getDefaultToolkit ().getScreenSize ();
int yCorrection = 0;
if (org.openide.util.Utilities.isWindows ()) {
yCorrection = org.netbeans.core.output.OutputTab.TYPICAL_WINDOWS_TASKBAR_HEIGHT;
}
if (p.x + popupSize.width > screenSize.width) p.x = screenSize.width - popupSize.width;
if (p.y + popupSize.height > screenSize.height - yCorrection) p.y = screenSize.height - popupSize.height - yCorrection;
SwingUtilities.convertPointFromScreen (p, MultiTabContainer.this);
popup.show(tabc, p.x, p.y);
}
} // end of PopupMouseImpl
/** Takes care of lazy renaming of tabs in tabbed panel.
* Waits until the name stays the same for some period of time,
* and only then calls rename of the tab.
*/
private static final class NameChanger extends Object implements Runnable {
/** Outer class */
private MultiTabContainer outer;
/** The component whose name has changed */
TopComponent component;
NameChanger (MultiTabContainer outer, TopComponent component) {
this.outer = outer;
this.component = component;
}
public synchronized void run () {
// update main title
outer.updateMainTitle();
String newName = component.getName();
if (outer.innerStatus == MULTI) {
// update tabbed pane
JTabbedPane tabc = outer.tabc;
int compIndex = tabc.indexOfComponent(component);
if (compIndex < 0)
return; // removed or somewhat..
tabc.setTitleAt(compIndex, newName);
tabc.invalidate();
tabc.validate();
tabc.repaint();
}
// notify name change listeners
outer.fireNameChange(newName);
}
} // end of NameChanger inner class
/** Basic version of persistence for mode implementation.
* Method assignData(modeImpl) must be called prior to serialization */
private static final class Version1
implements DefaultReplacer.ResVersionable {
/* identification string */
public static final String NAME = "Version_1.0"; // NOI18N
/** variables of persistent state of the mode implementation */
int innerStatus;
String workspaceName;
String modeName;
Rectangle bounds;
/**
* @associates TopComponentManager
*/
ArrayList tcManagers;
WindowManagerImpl.TopComponentManager currentTcm;
/** asociation with outerclass, used when writing */
MultiTabContainer mtc;
/** Identification of the version */
public String getName () {
return "Version_1.0"; // NOI18N
}
/** Assigns data to be written. Must be called before writing */
public void assignData (MultiTabContainer mtc) {
this.mtc = mtc;
}
/** read the data of the version from given input */
public void readData (ObjectInput in)
throws IOException, ClassNotFoundException {
// read the fields
innerStatus = ((Integer)in.readObject()).intValue();
workspaceName = (String)in.readObject();
modeName = (String)in.readObject();
bounds = (Rectangle)in.readObject();
// read managers of top components (but leave top components
// in byte streams, not fully deserialized).
// Deserialization of tcs will be finished
// in validateData() of MultiTabContainer
tcManagers = (ArrayList)in.readObject();
// remove tcManagers deserialized to null, because
// it signalizes that there was a problem during
// serialization of top component
WindowManagerImpl.TopComponentManager[] tcmArray =
(WindowManagerImpl.TopComponentManager[])
tcManagers.toArray(new WindowManagerImpl.TopComponentManager[tcManagers.size()]);
for (int i = tcmArray.length - 1; i >= 0; i--) {
if (tcmArray[i] == null) {
tcManagers.remove(i);
}
}
// obtain manager of selected tc
Object temp = in.readObject();
//System.out.println("Class: " + temp.getClass().getName()); // NOI18N
currentTcm = (WindowManagerImpl.TopComponentManager)temp;
}
/** write the data of the version to given output */
public void writeData (ObjectOutput out)
throws IOException {
// write fields
out.writeObject(new Integer(mtc.innerStatus));
out.writeObject(mtc.mode.getWorkspace().getName());
out.writeObject(mtc.mode.getName());
out.writeObject(mtc.mode.getBounds());
// create and write list of tc managers
tcManagers = new ArrayList();
TopComponent[] tcs = mtc.getTopComponents();
for (int i = 0; i < tcs.length; i++) {
tcManagers.add(WindowManagerImpl.findManager(tcs[i]));
}
out.writeObject(tcManagers);
// write manager of current tc
currentTcm = (mtc.current == null)
? null : WindowManagerImpl.findManager(mtc.current);
out.writeObject(currentTcm);
}
public Object resolveData ()
throws ObjectStreamException {
// create and fill mtc
MultiTabContainer result = new MultiTabContainer();
result.innerStatus = innerStatus;
result.workspaceName = workspaceName;
result.modeName = modeName;
result.setBounds(bounds);
result.tcManagers = tcManagers;
result.currentTcm = currentTcm;
return result;
}
} // end of Version1 inner class
/** Implementation of persistent access to our version serializator */
private static final class VSAccess implements DefaultReplacer.Access {
/** serialVersionUID */
private static final long serialVersionUID = -5417180019495806786L;
/** version serializator, used only during writing */
transient VersionSerializator vs;
public VSAccess (VersionSerializator vs) {
this.vs = vs;
}
public VersionSerializator getVersionSerializator () {
return (vs == null) ? createSerializationManager() : vs;
}
} // end of VSAccess inner class
}
/*
* Log
* 30 Gandalf 1.29 3/11/00 Martin Ryzl menufix [by E.Adams,
* I.Formanek]
* 29 Gandalf 1.28 1/15/00 David Simonek mutliwindow title bug
* fixed
* 28 Gandalf 1.27 1/13/00 David Simonek i18n
* 27 Gandalf 1.26 1/12/00 Ian Formanek NOI18N
* 26 Gandalf 1.25 1/12/00 Ales Novak setVisible added
* 25 Gandalf 1.24 1/12/00 David Simonek
* 24 Gandalf 1.23 1/10/00 David Simonek minor changes connected
* with focus problematics
* 23 Gandalf 1.22 12/23/99 David Simonek
* 22 Gandalf 1.21 12/17/99 David Simonek #1913, #2970
* 21 Gandalf 1.20 11/30/99 David Simonek neccessary changes
* needed to change main explorer to new UI style (tabs are full top
* components now, visual workspace added, layout of editing workspace
* chnaged a bit)
* 20 Gandalf 1.19 11/10/99 David Simonek debug comments removed
* 19 Gandalf 1.18 11/4/99 David Simonek ws serialization
* bugfixes
* 18 Gandalf 1.17 11/3/99 David Simonek completely rewritten
* serialization of windowing system...
* 17 Gandalf 1.16 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 16 Gandalf 1.15 10/7/99 David Simonek request focus related
* bugs repaired
* 15 Gandalf 1.14 10/6/99 David Simonek more robust
* serialization of window system (especially editor TCs)
* 14 Gandalf 1.13 9/13/99 David Simonek request focus for the
* mode added
* 13 Gandalf 1.12 8/18/99 David Simonek visibility bugfix
* 12 Gandalf 1.11 8/14/99 David Simonek bugfixes, #3347, #3274
* etc.
* 11 Gandalf 1.10 8/9/99 David Simonek
* 10 Gandalf 1.9 8/2/99 Ian Formanek popup menu positioning
* improved
* 9 Gandalf 1.8 8/2/99 Ian Formanek Fixed startup problem
* with deserialization of workspaces (IndexOutOfBoundsException thrown)
* 8 Gandalf 1.7 7/30/99 David Simonek window icons, comments
* removed
* 7 Gandalf 1.6 7/30/99 David Simonek serialization fixes
* 6 Gandalf 1.5 7/30/99 David Simonek iconification bugfixes,
* focus bugfixes
* 5 Gandalf 1.4 7/29/99 David Simonek further ws serialization
* changes
* 4 Gandalf 1.3 7/28/99 David Simonek serialization of window
* system...first draft :-)
* 3 Gandalf 1.2 7/21/99 David Simonek window system updates...
* 2 Gandalf 1.1 7/20/99 David Simonek various window system
* updates
* 1 Gandalf 1.0 7/11/99 David Simonek
* $
* Beta Change History:
* 0 Tuborg 0.30 --/--/98 Jaroslav Tulach Added lock and all synchronization is done against it.
* 0 Tuborg 0.30 --/--/98 Jaroslav Tulach Deadlock warning: Do not synchronize against the frame!!!!
* 0 Tuborg 0.31 --/--/98 Jan Formanek bugfix (lock was null after serialization)
* 0 Tuborg 0.32 --/--/98 Petr Hamernik resizing changed
* 0 Tuborg 0.33 --/--/98 Petr Hamernik context popup menu at the tabc
* 0 Tuborg 0.34 --/--/98 Petr Hamernik position is set by CoronaTopManager
* 0 Tuborg 0.35 --/--/98 Petr Hamernik bug fix
* 0 Tuborg 0.36 --/--/98 Jaroslav Tulach undoable edit
* 0 Tuborg 0.37 --/--/98 Jan Formanek improved popup menu invocation
* 0 Tuborg 0.38 --/--/98 Jan Jancura componentActivated / Deactivated support
* 0 Tuborg 0.39 --/--/98 Petr Hamernik modified flag
* 0 Tuborg 0.43 --/--/98 Petr Hamernik bugfix
* 0 Tuborg 0.44 --/--/98 Petr Hamernik positioning improvements
* 0 Tuborg 0.45 --/--/98 Miloslav Metelk patched deadlock for closeLast()
* 0 Tuborg 0.47 --/--/98 Petr Hamernik switching tabs - actions
* 0 Tuborg 0.48 --/--/98 Petr Hamernik bugfixes
* 0 Tuborg 0.50 --/--/98 Jan Formanek HelpContext
* 0 Tuborg 0.51 --/--/98 Jan Formanek repaint is setVisible ();
*/